SPDX-FileCopyrightText: 2018 Boris Bardonneau & Nestor Beguin SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be
SPDX-License-Identifier: GPL-3.0-or-later
SETUP CAMERA AND MODEL CLEANING# ###go line 100 for code
import bpy
import random
from random import choice
for o in bpy.data.objects: # nettoie aussi l'animation
o.hide = False
bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete(use_global=False)script realise sous Blender hach 4bb1e22 18.11.2016 par AlICelab CAMERA SCRIPT
import math
from math import radians
from math import cos
from math import sinDefinition de la fonction de création de camera en projection parallele
def axoCam(projection, canon):
bpy.ops.object.camera_add()
maScene = bpy.context.scene.render
monAxoCam = bpy.context.object
monAxoCam.data.type = "ORTHO"
monAxoCam.data.ortho_scale = 30
if projection == "axonometrique":
if canon == "isometrie": # OK
monAxoCam.name = "axoIsometrie"
monAxoCam.rotation_euler = (radians(54.74), 0.0, radians(45))
monAxoCam.location = (10.0, -10.0, 10.0)
maScene.pixel_aspect_x = 1
if canon == "dimetrie": # OK
monAxoCam.name = "axoDimetrie"
monAxoCam.rotation_euler = (radians(60), 0.0, radians(23))
monAxoCam.location = (5.53, -13.04, 8.18)
maScene.pixel_aspect_x = 1
if canon == "trimetrie": # OK
monAxoCam.name = "axoTrimetrie"
monAxoCam.rotation_euler = (radians(67), 0.0, radians(34))
monAxoCam.location = (8.59, -12.734, 6.52)
maScene.pixel_aspect_x = 1
if projection == "oblique":
if canon == "militaire": # OK
monAxoCam.name = "oblMilitaire"
monAxoCam.rotation_euler = (radians(45), 0.0, radians(30))
monAxoCam.location = (7.071, -12.247, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45))
if canon == "militaireDiminuee": # OK
monAxoCam.name = "oblMilitaireDiminuee"
monAxoCam.rotation_euler = (radians(45 * 0.82), 0.0, radians(30))
monAxoCam.location = (5.309, -9.195, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45 * 0.82))
if canon == "cavalierePlan": # OK
monAxoCam.name = "oblCavalierePlan"
monAxoCam.rotation_euler = (radians(45), 0.0, radians(45))
monAxoCam.location = (10.0, -10.0, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45))
if canon == "cavalierePlanDiminuee": # OK
monAxoCam.name = "oblCavalierePlanDiminuee"
monAxoCam.rotation_euler = (radians(45 * 0.82), 0.0, radians(45))
monAxoCam.location = (7.5, -7.5, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(radians(45 * 0.82))
if canon == "cavaliereAngleCam": # OK mais à recentrer manuellement
angleZ = radians(30) # entrer l'angle de rotation de la camera en Z
angleZ_XY = radians(
15
) # entrer l'angle d'inclinaison de la camera par rapport au sol
monAxoCam.name = "oblCavalierePlanDiminuee"
monAxoCam.rotation_euler = (angleZ_XY, 0.0, angleZ)
monAxoCam.location = (10, -10, 10 * 1 / cos(radians(45)))
maScene.pixel_aspect_x = 1 / cos(angleZ_XY)Execution de la fonction de création de la camera choix du type de projection: Effacer le # devant la ligne pour choisir le type de camera à créer
PROJECTION OBLIQUE#
axoCam (‘oblique’,’militaire’) axoCam (‘oblique’,’militaireDiminuee’) axoCam (‘oblique’,’cavalierePlan’) axoCam (‘oblique’,’cavalierePlanDiminuee’) axoCam (‘oblique’,’cavaliereAngleCam’) #celle ci peut être réglée en angle de vue -> regarder dans la dernière formule
PROJECTION AXONOMETRIQUE#
axoCam("axonometrique", "isometrie")axoCam (‘axonometrique’,’dimetrie’) axoCam (‘axonometrique’,’trimetrie’)
DIMENSIONEMENT DE L’ENVELOPPE GENERALE ET DU CENTRE DU SOCLE ( CUBE )
Rectanglex = [10]
Rectangley = [10]
Rectangleposx = [5]
Rectangleposy = [5]def Division(x, y, px, py, i): # SEGMENTATION DU SOCLE EN MUSICIENS
choix = 1 if x > y else 0 # Détermine le plus grand rectangle de la liste
if choix == 1: # Segmente en x
xvar = random.uniform(0, x)
xvar2 = x - xvar
yvar = y
yvar2 = y
posx = px - x / 2 + xvar / 2
posy = py
posx2 = px - x / 2 + xvar + xvar2 / 2
posy2 = py
else: # Segmente en y
yvar = random.uniform(0, y)
yvar2 = y - yvar
xvar = x
xvar2 = x
posx = px
posy = py - y / 2 + yvar / 2
posx2 = px
posy2 = py - y / 2 + yvar + yvar2 / 2
Rectanglex[i] = xvar # Change la valeur du rectangle de base
Rectangley[i] = yvar
Rectangleposx[i] = posx
Rectangleposy[i] = posy
Rectanglex.append(xvar2) # Ajoute le second rectangle créé
Rectangley.append(yvar2)
Rectangleposx.append(posx2)
Rectangleposy.append(posy2)def Phrasemusicale(
x, y, z, px, py, h, t
): # Choix des différente formes de base pour définir les phrases musicales
if t == 0: # rectangle dimensions 100%
bpy.ops.mesh.primitive_cube_add(radius=0.5, location=(px, py, z))
bpy.ops.transform.resize(value=(x, y, h))
if t == 3: # rectangle dimensions 100%2 pour plus de ratio
bpy.ops.mesh.primitive_cube_add(radius=0.5, location=(px, py, z))
bpy.ops.transform.resize(value=(x, y, h))
if t == 6: # cylindre dimension 50%
bpy.ops.mesh.primitive_cylinder_add(radius=0.5, depth=1, location=(px, py, z))
bpy.ops.transform.resize(value=(x / 2, y / 2, h))
if t == 2: # cylindre dimension 25% et décalé en x+
bpy.ops.mesh.primitive_cylinder_add(radius=0.5, depth=1, location=(px, py, z))
bpy.ops.transform.resize(value=(x / 8, y / 8, h))
if t == 4: # cylindre dimension 25% et décalé en x-
bpy.ops.mesh.primitive_cylinder_add(radius=0.5, depth=1, location=(px, py, z))
bpy.ops.transform.resize(value=(x / 8, y / 8, h))
if t == 1: # rectangle dimension 50%
bpy.ops.mesh.primitive_cube_add(radius=0.5, location=(px, py, z))
bpy.ops.transform.resize(value=(x / 6, y / 635, h))
bpy.context.object.name = "Phrase" + str(z) # nomenclature de la formeMusiciens = 40 # Nombre de musiciens désirés
Mesures = 40 # Longueur de la chanson
hauteur = 10 / Mesures # hauteur des phrases
frame_num = 0 # animation
animation_speed = 1 # Vitesse de l'animation(Nombre de frames par niveau)
Temps_video = 20 # temps de la video en seconde
Phrases_Count = 53 # Nombres de phrases musicales
TransMin = 1 # Scale pour phrase musicale minimum
TransMax = 2 # Scale pour phrase musicale maximum
typemax = 4 # nombre de type de formes différentes
sens = True # sens de départ d'aparitiin
seuilonoff = 5 # seuil% de chance de passer on/off
seuilmin = 30 # nombre de musicien actif max
seuilmax = 90 # nombre de musicien actif minbpy.context.scene.render.fps = Mesures / Temps_video # frequence d'encodage
bpy.context.scene.frame_end = Mesures * animation_speed # Nombre d'image autocalculé
bpy.context.scene.frame_step = animation_speed # Vitesse d'image autocalculébpy.ops.mesh.primitive_plane_add(location=(0,0,-4.9)) #Ajout d’un socle pour recevoir les ombres bpy.ops.transform.resize(value= (100,100,0))
bpy.ops.object.lamp_add(
type="SUN", location=(10, 10, 20)
) # ajout du soleil et orientation
bpy.ops.transform.rotate(
value=0.727953,
axis=(0, 1, 0),
constraint_axis=(False, True, False),
constraint_orientation="GLOBAL",
mirror=False,
proportional="DISABLED",
proportional_edit_falloff="SMOOTH",
proportional_size=1,
release_confirm=True,
use_accurate=False,
)bpy.ops.transform.rotate(value=-0.727953, axis=(1, 0, 0), constraint_axis=(True, False, False), constraint_orientation=’GLOBAL’, mirror=False, proportional=’DISABLED’, proportional_edit_falloff=’SMOOTH’, proportional_size=1, release_confirm=True, use_accurate=False)
bpy.context.object.data.shadow_method = "RAY_SHADOW"Coordonees = [] # Position des musiciens
Statut = [] # Musiciens actifs, inactifs
Phrases_musicales_taille = [] # Phrases musciales_taille
Phrases_musicales_type = [] # Phrases musciales_type de forme
Phrases_musicales_Index = [] # position du musicien dans la liste des phrases muscialesfor i in range(0, Musiciens):
max_x, max_y = max(Rectanglex), max(Rectangley)
i = Rectanglex.index(max_x) if max_x > max_y else Rectangley.index(max_y)
Division(Rectanglex[i], Rectangley[i], Rectangleposx[i], Rectangleposy[i], i)for i in range(
Phrases_Count
): # Crée un liste de phrases musicale de différent type et tailles
Phrases_musicales_taille.append(random.uniform(TransMin, TransMax))
Phrases_musicales_type.append(random.randint(0, typemax))for i in range(0, len(Rectanglex)):
Statut.append(random.randint(0, 1)) # Commence actif ou inactif
Phrases_musicales_Index.append(1) # Commence par la 1ere phrase musicaleAPPLICATION
for z in range(0, Mesures): # Pour autant de mesures(z)
print("Mesure", z)
var1 = Statut.count(1) # Nombre de musiciens Actifs
var2 = len(Statut) # Nombre total de musiciens
musicienactifs = var1 / var2 * 100 # % de musicien actif
print(musicienactifs, "%", " actif")
if musicienactifs < seuilmin: # passe en mode ascendant
sens = True
if musicienactifs > seuilmax: # passe en mode descendant
sens = False
for i in range(0, len(Rectanglex)): # pour tous les musiciens un par un par étage
if Statut[i] == 1: # Si musicien actifs’arrete si musicien a fini les phrases
if Phrases_musicales_Index[i] == Phrases_Count - 1:
continue
else: Phrasemusicale(
Rectanglex[i],
Rectangley[i],
z * hauteur - 5,
Rectangleposx[i] - 5,
Rectangleposy[i] - 5,
hauteur,
Phrases_musicales_type[Phrases_musicales_Index[i]],
)
bpy.ops.transform.resize(
value=(
Phrases_musicales_taille[Phrases_musicales_Index[i]],
Phrases_musicales_taille[Phrases_musicales_Index[i]],
1,
)
) obj = bpy.context.active_objectkey as visible on the current frame
obj.keyframe_insert("hide", frame=z * animation_speed)
obj.keyframe_insert("hide_render", frame=z * animation_speed)hide it
obj.hide = True
obj.hide_render = Truekey as hidden on the previous frame
obj.keyframe_insert(
"hide", frame=z * animation_speed - 1 * animation_speed
)
obj.keyframe_insert(
"hide_render", frame=z * animation_speed - 1 * animation_speed
)key as hidden on the next frame obj.keyframe_insert(‘hide’,frame=z+1) obj.keyframe_insert(‘hide_render’,frame=z+1)
if sens: # si ascendant
if not Statut[i]:
if random.randint(0, 100) <= seuilonoff: # % de chance de passer actifprint(‘actif’)
Statut[i] = 1
if not sens:si musicien actif
if Statut[i]:
if (
random.randint(0, 100) <= seuilonoff
): # % de chance de passer inactifprint(‘inactif’)
Statut[i] = 0ne s’applique pas si le musicien a fini ses phrases
if Phrases_musicales_Index[i] == Phrases_Count - 1:
continue
else:
if Phrases_musicales_Index[i] >= 5: # Si musicien au dessus de la 5e phrase
Analyse = []
for analysis in range(
3, 6
): # analyse le nombre de musiciens sur les phrases situées trois index plus bas, et quatre index après
Analyse.append(
Phrases_musicales_Index.count(
Phrases_musicales_Index[i] - analysis
)
)print (Analyse)
if (
Analyse.count(0) == 3
): # s'il n'y en a plus, il peut passer au suivantprint(‘analyse ok’)
if (
random.randint(0, 100) < 60
): # Choix du musicien si il change de phrase musicale
Phrases_musicales_Index[i] = Phrases_musicales_Index[i] + 1print ( ‘phrase musicale+1’)
if (
Phrases_musicales_Index[i] < 5
): # Les cinq premières phrases ne risque pas d'aller trop vite
if (
random.randint(0, 100) < 70
): # % de chance pour le musicien de changer de phrase musicale
Phrases_musicales_Index[i] = Phrases_musicales_Index[i] + 1print (Phrases_musicales_Index[i])